package edu.kit.aifb.ldstream.learner.tasks;

import java.util.HashSet;
import java.util.Map;
import java.util.logging.Logger;

import org.openrdf.model.BNode;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.query.BindingSet;
import org.openrdf.query.impl.MutableTupleQueryResult;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.rio.ParserConfig;
import org.openrdf.rio.RDFParser.DatatypeHandling;

import edu.kit.aifb.ldstream.env.Environment;
import edu.kit.aifb.ldstream.learner.dao.Instance;
import edu.kit.aifb.ldstream.learner.dao.SimpleStatistics;
import edu.kit.aifb.ldstream.learner.dao.Type;
import edu.kit.aifb.ldstream.learner.query.SPARQLQuery;

public class LearnClassesTask extends LearnerTask {

	private Map<Type, HashSet<Instance>> classMap;
	private SimpleStatistics stats;
	private int size = 0;

	private Logger log;
	
	public LearnClassesTask(SimpleStatistics stats, Map<Type, HashSet<Instance>> classMap){
		this.stats = stats;
		this.classMap = classMap;
		this.log = Logger.getLogger(this.getClass().getName());
	}


	@Override
	public void run() {
		learnClasses();
	}


	private void learnClasses(){
		String queryString = "SELECT $subj WHERE { $subj $pred <" + RDFS.CLASS.stringValue()+ ">  }";//Filter(!isBlank($subj))
		try{
			RepositoryConnection con = Environment.REPO.getConnection();
			con.setParserConfig(new ParserConfig(true, true, true, DatatypeHandling.VERIFY));
			SPARQLQuery query = new SPARQLQuery(con, queryString, false);
			query.disableAutoClose();
			MutableTupleQueryResult result = query.call();
			
			int tenth = Math.max(result.size() / 10, 1);
			double percentage = 100d / result.size();
			int i = 1;
			
			BindingSet set;
			Value subj;
			Type type;
			try{
				while(result.hasNext()){
					if( (i % tenth) == 0){
						log.info("Classes learned to: " + Math.round(i * percentage) + "%");
					}
					i++;
					set = result.next();
					subj = set.getValue("subj");
					if(subj instanceof BNode){
						learnClasses(con, (BNode) subj);
					}else{
						type = new Type((URI) subj);
						if(!this.classMap.containsKey(type)){ 
							this.classMap.put(type, new HashSet<Instance>());
							this.size++;
						}
					}
				}
			}finally{
				result.close();
				con.close();
			}
			if(Environment.VERBOSE){
				System.out.println("NumTypes: " + this.size);
			}
			this.stats.setNumTypes(this.size);

		}catch(RepositoryException e){
			e.printStackTrace();
		}
	}

	private void learnClasses(RepositoryConnection con, BNode b){
		
		try {
			RepositoryResult<Statement> result = con.getStatements(null, null, b, false);
			try{
				Statement s;
				Type type;
				Value subj;
				while(result.hasNext()){
					s = result.next();
					subj = s.getSubject();
					if(subj instanceof BNode){
						learnClasses(con, (BNode) subj);
					}else {
						type = new Type((URI) subj);
						if(!this.classMap.containsKey(type)){ 
							this.classMap.put(type, new HashSet<Instance>());
							this.size++;
						}
					}
				}
			}finally{
				result.close();
			}
		} catch (RepositoryException e) {
			e.printStackTrace();
		}
		
	}
	
}
